// LAMath - A SIMD-based Linear Algebra library
//
// Copyright (C) 2010  William K Killian
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//  The creator of this project can be contacted at william.killian@gmail.com
//

#ifndef LAMATH_H
#define LAMATH_H

#include <xmmintrin.h>
#include <ostream>

// If you wish to use SSE4, please uncomment the following line
// #define USE_SSE4

namespace lamath
{
    ////////////////////////////////////////////////////////////////////////////////
    //
    // LAVector
    //   
    //   Provides SIMD instruction set for vector operations
    //   
    //   - Operations on Vectors:
    //      * Length
    //      * Length Squared
    //      * Normalization
    //      * Negation
    //
    //   - Scalar Operations on Vectors
    //      * Addition
    //      * Subtraction
    //      * Multiplication
    //      * Division
    //
    //   - Vector Operations on Vectors
    //      * Addition
    //      * Subtraction
    //      * Dot Product
    //      * Cross Product
    //
    ////////////////////////////////////////////////////////////////////////////////
    
    class vector;
  
    ////////////////////////////////////////////////////////////////////////////////
    //
    // LAMatrix
    //   
    //   Provides SIMD instruction set for Matrix operations
    //
    //   - Operations on Matricies
    //      * Inverse
    //      * Transpose
    //   
    //   - Scalar Operations on Matricies
    //      * Addition
    //      * Subtraction
    //      * Multiplication
    //      * Division
    //
    //   - Vector Operations on Matricies
    //      * Multiplication
    //
    //   - Matrix Operations on Matricies
    //      * Addition
    //      * Subtraction
    //      * Multiplication
    //
    ////////////////////////////////////////////////////////////////////////////////

    class matrix;
    
    ////////////////////////////////////////////////////////////////////////////////
    // 
    // Class Documentation
    //
    ////////////////////////////////////////////////////////////////////////////////
    
    class vector
    {
	
    public:
      
	union
	{
	    __m128 m;
	    struct
	    {
		float x, y, z, w;
	    };
	    float array [4];
	};
	
	// Default constructor; not guaranteed to zero out elements
	//
	vector ();
	
	// Initializes all vector components to be equal to passed value
	//
        // Parameters:
	//   float - value to assign to all components
	//   
	vector (float default_value);
	
	// Initializes vector components to be equal to appropriate passed value
	//
	// Parameters:
	//   float   - x component value
	//   float   - y component value
	//   float   - z component value
	//   [float] - w component value
	//
	vector (float x, float y, float z, float w = 0.0);
	
	// Initializes vector data member to passed 128-bit register
	//
        // Parameters:
	//   __m128 - register to assign
	//
	vector (__m128 x);
	
	// Index Operator. Access value at specified index
	//
	// Parameters:
	//   size_t - index to access
	//
	// Returns:
	//   float - reference to value at specified index
	//
	inline
	float&
	operator() (size_t index);
    
	// Index Operator. Access value at specified index
	//
	// Parameters:
	//   size_t - index to access
	//
	// Returns:
	//   float - value at specified index
	//
	inline
	float
	operator() (size_t index) const;
	
	// Return the positive form of the vector
	//
	// Returns:
	//   vector - the resulting vector
	//
	inline
	vector
	operator+() const;
	
	// Return the negatation of the vector
	// 
	// Returns:
	//   vector - the resulting vector
	//
	inline
	vector
	operator-() const;
	
	// Add a vector to the current vector
	// 
	// Parameters:
	//   vector - the vector to add to the current
	//
	// Returns:
	//   vector - the resulting vector
	//
	inline
	vector&
	operator+= (const vector &other);
	
	// Subtract a vector from the current vector
	// 
	// Parameters:
	//   vector - the vector to subtract to the current
	//
	// Returns:
	//   vector - the resulting vector
	//
	inline
	vector&
	operator-= (const vector &other);
	
	// Vector comparison for equality
	//
	// Parameters:
	//   vector - vector to compare current vector to
	//
	// Returns:
	//   bool - true if vectors are equal; false otherwise
	//
	inline
	bool
	operator== (const vector &other) const;
    
	// Vector comparison for inequality
	//
	// Parameters:
	//   vector - vector to compare current vector to
	//
	// Returns:
	//   bool - true if vectors are not equal; false otherwise
	//
	inline
	bool
	operator!= (const vector &other) const;
	
	// Length of vector
	//
	// Returns:
	//   float - the length of the vector
	//
	inline
	float
	length () const;
	
	// Length squared of vector
	//
	// Returns:
	//   float - the length of the vector
	//
	inline
	float
	length_squared () const; 
	
	// Normalizes the vector
	// 
	// Returns:
	//   none
	//
	inline
	void
	normalize ();
	
	// Scales a passed vector by a value
	//
	// Parameters:
	//   vector - vector to apply operation
	//   float  - value to scale vector by
	//
	// Returns:
	//   vector - the resulting vector
	//
	static
	inline
	vector
	multiply (const vector &v, float s);
    
	// Divides a passed vector by a value
	//
	// Parameters:
	//   vector - vector to apply operation
	//   float  - value to divide vector by
	//
	// Returns:
	//   vector - the resulting vector
	//
	static
	inline
	vector
	divide (const vector &v, float s);
	
	// Adds two passed vectors
	//
	// Parameters:
	//   vector - vector to apply operation
	//   vector - the second vector in the operation
	//
	// Returns:
	//   vector - the resulting vector
	//
	static
	inline
	vector
	add (const vector &v1, const vector &v2);
	
	// Subtracts two passed vectors
	//
	// Parameters:
	//   vector - vector to apply operation
	//   vector - the second vector in the operation
	//
	// Returns:
	//   vector - the resulting vector
	//
	static
	inline
	vector
	subtract (const vector &v1, const vector &v2);
	
	// Computes the dot product of two passed vectors
	//
	// Parameters:
	//   vector - vector to apply operation
	//   vector - the second vector in the operation
	//
	// Returns:
	//   vector - the resulting vector
	//
	static
	inline
	float
	dot (const vector &v1, const vector &v2);
    
	// Computes the cross product of two passed vectors
	//
	// Parameters:
	//   vector - the first vector in the operation
	//   vector - the second vector in the operation
	//
	// Returns:
	//   vector - the resulting vector
	//
	static
	inline
	vector
	cross (const vector &v1, const vector &v2);
	
	// Obtains the length of the passed vector
	//
	// Parameters:
	//   vector - vector to apply operation
	//   
	// Returns:
	//   vector - the resulting vector
	//
	static
	inline
	float
	length (const vector &v);
    
	// Obtains the length squared of the passed vector
	//
	// Parameters:
	//   vector - vector to apply operation
	//   
	// Returns:
	//   vector - the resulting vector
	//
	static
	inline
	float
	length_squared (const vector &v);
	
	// Normalized the passed vector
	//
	// Parameters:
	//   vector - vector to normalize
	//
	// Returns:
	//   vector - the normalized vector
	//
	static
	inline
	vector
	normalize (const vector &v);
	
    };
    
    
    class matrix
    {
	
    public:
	
	union
	{
	    __m128 m[4];
	    struct
	    {
		float m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33; 
	    };
	    float array [4][4];
	};
	
	// Default constructor; not guaranteed to zero out elements
	//
	matrix ();
	
	// Initializes all matrix components to be equal to passed value
	//
        // Parameters:
	//   float - value to assign to all components
	//   
	matrix (float default_val);
	
	// Initializes all matrix components to be equal to passed matrix
	//
	// Parameters:
	//   matrix - the matrix to copy all components from
	//
	matrix (const matrix &other);
	
	
	// Initializes vector data member to passed 128-bit register array
	//
        // Parameters:
	//   __m128[] - register array to assign
	//
	matrix (const __m128 x[]);
	
	
	// Initializes vector data member to passed 128-bit register array
	//
	// Parameters:
	//   __m128 - register to set as 1st row
	//   __m128 - register to set as 2nd row
	//   __m128 - register to set as 3rd row
	//   __m128 - register to set as 4th row
	//
	matrix (const __m128 r0, const __m128 r1, const __m128 r2, const __m128 r3);
	
	
	// Initializes vector components to be equal to appropriate passed value
	//
	// Parameters:
	//   float - m00 component value
	//   float - m01 component value
	//   float - m02 component value
	//   float - m03 component value
	//   float - m10 component value
	//   float - m11 component value
	//   float - m12 component value
	//   float - m13 component value
	//   float - m20 component value
	//   float - m21 component value
	//   float - m22 component value
	//   float - m23 component value
	//   float - m30 component value
	//   float - m31 component value
	//   float - m32 component value
	//   float - m33 component value
	//
	matrix (float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, float m21, float m22, float m23, float m30, float m31, float m32, float m33);
    
	// Assigns passed matrix to current matrix
	//
	// Parameters:
	//   matrix - the matrix to copy all components from
	//
	inline
	matrix&
	operator= (const matrix &other);
	
	// Element Access/Mutator Operator
	//
	// Parameters:
	//   size_t - row to access
	//   size_t - column to access
	//
	// Returns:
	//   float - reference to the float value at the specified row and column
	//
	inline
	float&
	operator() (size_t row, size_t col);
	
	// Element Access Operator
	//
	// Parameters:
	//   size_t - row to access
	//   size_t - column to access
	//
	// Returns:
	//   float - the float value at the specified row and column
	//
	inline
	float
	operator() (size_t row, size_t col) const;
	
	// Row Access
	//
	// Parameter:
	//   size_t - row to access
	// 
	// Returns:
	//   vector - the elements stored at the passed row
	//
	inline
	vector
	row (size_t row) const;
	
	// Column Access
	//
	// Parameter:
	//   size_t - column to access
	//
	// Returns:
	//   vector - the elements stored at the passed column
	//
	inline
	vector
	column (size_t col) const;
	
	// Negation Operator
	//
	// Returns:
	//   matrix - a copy of the matrix with all components negated
	//
	inline
	matrix
	operator- () const;
	
	// Positive Operator
	//
	// Returns:
	//   matrix - a copy of the matrix with all components the same
	//
	inline
	matrix
	operator+ () const;
	
	// Add a matrix to the current matrix
	//
	// Parameters:
	//   matrix - the matrix to add to the current
	//
	// Returns:
	//   matrix - the resulting matrix
	//
	inline
	matrix&
	operator += (const matrix &other);
	
	
	// Subtract a matrix from the current matrix
	//
	// Parameters:
	//   matrix - the matrix to subtract to the current
	//
	// Returns:
	//   matrix - the resulting matrix
	//
	inline
	matrix&
	operator -= (const matrix &other);
	
	// Equality Test
	//
	// Parameters:
	//   matrix - matrix to compare equality with
	//
	// Returns:
	//   boolean - true if matrices are equal; false otherwise
	//
	inline
	bool
	operator== (const matrix &m) const;
	
	// Inequality Test
	//
	// Parameters:
	//   matrix - matrix to compare equality with
	//
	// Returns:
	//   boolean - true if matrices are equal; false otherwise
	//
	inline
	bool
	operator!= (const matrix &m) const;
	
	// Matrix Inverse
	// 
	// Returns:
	//   matrix - inverse of the current matrix
	//
	inline
	matrix
	inverse () const;
		
	
	// Matrix Transpose
	// 
	// Returns:
	//   matrix - transpose of the current matrix
	//
	inline
	matrix
	transpose () const;
	
	// Scales a passed matrix by a value
	//
	// Parameters:
	//   matrix - matrix to apply operation
	//   float  - value to scale matrix by
	//
	// Returns:
	//   matrix - the resulting matrix
	//
	static
	inline
	matrix
	multiply (const matrix &m, float s);
    
	
	// Inverse Scales a passed matrix by a value
	//
	// Parameters:
	//   matrix - matrix to apply operation
	//   float  - value to scale matrix by
	//
	// Returns:
	//   matrix - the resulting matrix
	//
	static
	inline
	matrix
	divide (const matrix &m, float s);
	
	// Adds two passed matricies together
	//
	// Parameters:
	//   matrix - first matrix to add
	//   matrix - second matrix to add
	//
	// Returns:
	//   matrix - the result of the matrix addition
	//
	static
	inline
	matrix
	add (const matrix &m1, const matrix &m2);
    
	// Subtracts two passed matricies together
	//
	// Parameters:
	//   matrix - first matrix to subtract
	//   matrix - second matrix to subtract
	//
	// Returns:
	//   matrix - the result of the matrix subtraction
	//
	static
	inline
	matrix
	subtract (const matrix &m1, const matrix &m2);
	
	// Multiplies two passed matricies together
	//
	// Parameters:
	//   matrix - first matrix to multiply
	//   matrix - second matrix to multiply
	//
	// Returns:
	//   matrix - the result of the matrix multiplication
	//
	static
	inline
	matrix
	multiply (const matrix &m1, const matrix &m2);
	
	// Calculates the matrix inverse of the passed matrix
	// 
	// Parameters:
	//   matrix - matrix to calculate inverse
	//
	// Returns:
	//   matrix - inverse of the passed matrix
	//
	static
	inline
	matrix
	inverse (const matrix &m);
	
	// Calculates the matrix inverse of the passed matrix
	// 
	// Parameters:
	//   matrix - matrix to calculate inverse
	//
	// Returns:
	//   matrix - inverse of the passed matrix
	//
	static
	inline
	matrix
	transpose (const matrix &m);
	
    };

}

////////////////////////////////////////////////////////////////////////////////
//
// Free Functions
//
////////////////////////////////////////////////////////////////////////////////

// Matrix-Vector Multiplication
//
// Parameters:
//   matrix - the matrix used
//   vector - the vector used
//
// Returns:
//   vector - the result of the matrix vector multiplication
//
inline
lamath::vector
operator* (const lamath::matrix &m, const lamath::vector &v);

// Matrix-Matrix Multiplication
//
// Parameters:
//   matrix - the first matrix
//   matrix - the second matrix
//
// Returns:
//   matrix - the result of the matrix matrix multiplication
//
inline
lamath::matrix
operator* (const lamath::matrix &m1, const lamath::matrix &m2);

// Vector-Scalar Multiplication
//
// Parameters:
//   vector - the vector used
//   float - the scalar value used
//
// Returns:
//   vector - the result of the vector scalar multiplication
//
inline
lamath::vector
operator* (const lamath::vector &v, float s);

// Scalar-Vector Multiplication
//
// Parameters:
//   float - the scalar value used
//   vector - the vector used
//
// Returns:
//   vector - the result of the scalar vector multiplication
//
inline
lamath::vector
operator* (float s, const lamath::vector &v);

// Matrix-Scalar Multiplication
//
// Parameters:
//   matrix - the matrix used
//   float - the scalar value used
//
// Returns:
//   matrix - the result of the matrix scalar multiplication
//
inline
lamath::matrix
operator* (const lamath::matrix &m, float s);

// Scalar-Matrix Multiplication
//
// Parameters:
//   float - the scalar value used
//   matrix - the matrix used
//
// Returns:
//   matrix - the result of the scalar matrix multiplication
// 
inline
lamath::matrix
operator* (float s, const lamath::matrix &v);

// Vector-Scalar Division
//
// Parameters:
//   vector - the vector used
//   float - the scalar value used
//
// Returns:
//   vector - the result of the vector-scalar division
//
inline
lamath::vector
operator/ (const lamath::vector &v, float s);

// Matrix-Scalar Division
//
// Parameters:
//   matrix - the matrix used
//   float - the scalar value used
//
// Returns:
//   matrix - the result of the matrix-scalar division
//
inline
lamath::matrix
operator/ (const lamath::matrix &v, float s);

// Vector-Vector Addition
//
// Parameters:
//   vector - the first vector
//   vector - the second vector
//
// Returns:
//   vector - the result of the addition
//
inline
lamath::vector
operator+ (const lamath::vector &v1, const lamath::vector &v2);

// Matrix-Matrix Addition
//
// Parameters:
//   matrix - the first matrix
//   matrix - the second matrix
//
// Returns:
//   matrix - the result of the addition
//
inline
lamath::matrix
operator+ (const lamath::matrix &m1, const lamath::matrix &m2);

// Vector-Vector Subtraction
//
// Parameters:
//   vector - the first vector
//   vector - the second vector
//
// Returns:
//   vector - the result of the subtraction
//
inline
lamath::vector
operator- (const lamath::vector &v1, const lamath::vector &v2);

// Matrix-Matrix Subtraction
//
// Parameters:
//   matrix - the first matrix
//   matrix - the second matrix
//
// Returns:
//   matrix - the result of the subtraction
//
inline
lamath::matrix
operator- (const lamath::matrix &m1, const lamath::matrix &m2);

// Prints out a vector
//
// Parameters:
//   ostream - output stream
//   vector  - vector to print
//
// Returns:
//   ostream - reference to the modified output stream
//
std::ostream&
operator<< (std::ostream &out, const lamath::vector &v);

// Prints out a matrix
//
// Parameters:
//   ostream - output stream
//   matrix  - matrix to print
//
// Returns:
//   ostream - reference to the modified output stream
//
std::ostream&
operator<< (std::ostream &out, const lamath::matrix &m);



// include implementation
#include "lamath.hpp"

#endif
